/*
 * InteractWindow.java
 *
 * Created on May 15, 2010, 5:46:53 PM
 */
package msfgui;

import java.awt.Font;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JOptionPane;
import org.jdesktop.swingworker.SwingWorker;

/**
 *
 * @author scriptjunkie
 */
public class InteractWindow extends MsfFrame {
	public final ReentrantLock lock = new ReentrantLock();
	public static final char POLL = 'r';
	public static final char STOP_POLLING = 's';
	private final Map session;
	private final RpcConnection rpcConn;
	private String type;
	private StringBuffer timerCommand;//synchronized mutable object as command placeholder for polling thread
	private static ArrayList commands;
	private static int currentCommand = 0;
	static{
		commands = new ArrayList();
		commands.add("");
	}

	/** Creates a new window for interacting with shells/meterpreters */
	public InteractWindow(final RpcConnection rpcConn, final Map session, final String type) {
		super(type+" interaction window");
		initComponents();
		this.rpcConn = rpcConn;
		this.session = session;
		this.type = type;
		timerCommand = new StringBuffer(""+POLL);

		//start new thread polling for input
		new SwingWorker() {
			protected Object doInBackground() throws Exception {
				long time = 100;
				while (timerCommand.charAt(0) == POLL) {
					if (lock.tryLock() == false) {
						this.publish("locked");
						lock.lock();
						this.publish("unlocked");
					}
					try {
						long start = System.currentTimeMillis();
						Map received = (Map) rpcConn.execute("session." + type + "_read", new Object[]{session.get("id")});
						time = System.currentTimeMillis() - start;
						if (!received.get("encoding").equals("base64"))
							throw new MsfException("Uhoh. Unknown encoding. Time to update?");
						byte[] decodedBytes = Base64.decode(received.get("data").toString());
						if (decodedBytes.length > 0) {
							outputArea.append(new String(decodedBytes));
							if(decodedBytes[decodedBytes.length-1] != '\n')
								outputArea.append("\n");//cause windows is just like that.
							publish("data");
						}
					} catch (MsfException ex) {
						if(!ex.getMessage().equals("unknown session"))
							JOptionPane.showMessageDialog(null, ex);
						timerCommand.setCharAt(0, STOP_POLLING);
					}
					lock.unlock();
					try {
						Thread.sleep(100 + (time * 3));// if it takes a long time to get data, ask for it slower
					} catch (InterruptedException iex) {
					}
				}
				return null;
			}
			protected void process(List l){
				for(Object o : l){
					if(o.equals("locked")){
						submitButton.setEnabled(false);
						inputField.setEditable(false);
					}else if(o.equals("unlocked")){
						submitButton.setEnabled(true);
						inputField.setEditable(true);
					}else{
						outputArea.setCaretPosition(outputArea.getDocument().getLength());
					}
				}
			}
		}.execute();
		
		if(type.equals("meterpreter"))
			inputField.setText("help");
		outputArea.setFont(new Font("Monospaced", outputArea.getFont().getStyle(), 12));
	}

	/** This method is called from within the constructor to
	 * initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is
	 * always regenerated by the Form Editor.
	 */
	@SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        outputScrollPane = new javax.swing.JScrollPane();
        outputArea = new javax.swing.JTextArea();
        inputField = new javax.swing.JTextField();
        submitButton = new javax.swing.JButton();

        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosed(java.awt.event.WindowEvent evt) {
                formWindowClosed(evt);
            }
            public void windowOpened(java.awt.event.WindowEvent evt) {
                formWindowOpened(evt);
            }
        });

        outputScrollPane.setAutoscrolls(true);
        outputScrollPane.setName("outputScrollPane"); // NOI18N

        outputArea.setColumns(20);
        outputArea.setEditable(false);
        outputArea.setRows(5);
        outputArea.setName("outputArea"); // NOI18N
        outputScrollPane.setViewportView(outputArea);

        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(msfgui.MsfguiApp.class).getContext().getResourceMap(InteractWindow.class);
        inputField.setText(resourceMap.getString("inputField.text")); // NOI18N
        inputField.setName("inputField"); // NOI18N
        inputField.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                inputFieldActionPerformed(evt);
            }
        });
        inputField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                inputFieldKeyPressed(evt);
            }
        });

        submitButton.setText(resourceMap.getString("submitButton.text")); // NOI18N
        submitButton.setName("submitButton"); // NOI18N
        submitButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                submitButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(inputField, javax.swing.GroupLayout.DEFAULT_SIZE, 657, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(submitButton))
                    .addComponent(outputScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 737, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(outputScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 533, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(inputField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(submitButton))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

	private void inputFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_inputFieldActionPerformed
		try {
			String command = inputField.getText();
			commands.add(command);
			String data = Base64.encode((command+"\n").getBytes());
			rpcConn.execute("session."+type+"_write", new Object[]{session.get("id"),data});
			outputArea.append(">>>"+command+"\n");
			outputArea.setCaretPosition(outputArea.getDocument().getLength());

			inputField.setText("");
			currentCommand = 0;
		} catch (MsfException ex) {
			JOptionPane.showMessageDialog(null, ex);
		}
	}//GEN-LAST:event_inputFieldActionPerformed

	private void submitButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_submitButtonActionPerformed
		inputFieldActionPerformed(evt);
	}//GEN-LAST:event_submitButtonActionPerformed

	private void formWindowClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowClosed
		timerCommand.setCharAt(0, STOP_POLLING);
	}//GEN-LAST:event_formWindowClosed

	private void inputFieldKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_inputFieldKeyPressed
		if(evt.getKeyCode() == KeyEvent.VK_UP){
			currentCommand = (currentCommand - 1 + commands.size()) % commands.size();
			inputField.setText(commands.get(currentCommand).toString());
		}else if(evt.getKeyCode() == KeyEvent.VK_DOWN){
			currentCommand = (currentCommand + 1) % commands.size();
			inputField.setText(commands.get(currentCommand).toString());
		}
	}//GEN-LAST:event_inputFieldKeyPressed

	private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
		inputField.requestFocusInWindow();
	}//GEN-LAST:event_formWindowOpened

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JTextField inputField;
    private javax.swing.JTextArea outputArea;
    private javax.swing.JScrollPane outputScrollPane;
    private javax.swing.JButton submitButton;
    // End of variables declaration//GEN-END:variables
}
